#include <functional>
#include <mutex>
#include <QMessageBox>

#include "app.h"
#include "tcpserverform.h"
#include "ui_tcpserverform.h"
#include "TcpServer.h"
#include "tcpsession.h"

TcpServerForm::TcpServerForm(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::TcpServerForm),
    m_Port(9004)
{
    ui->setupUi(this);
    InitForm();

    m_Server = new TcpServer();
    m_Server->onAccepted = std::bind(&TcpServerForm::AcceptSession, this, std::placeholders::_1);
}

TcpServerForm::~TcpServerForm()
{
    if(bIsListen) {
        if(m_Server->IsStart()) {
            m_Server->Stop();
            m_Server->onAccepted = nullptr;
        }
    }
    MapClear();

    delete m_Server;
    delete ui;
}

void TcpServerForm::InitForm()
{
    ui->ListenButton->setText(QStringLiteral("开始监听"));
    //ui->PortLineEdit->setPlaceholderText("9004");
    ui->PortLineEdit->setText("9004");
    ui->PortLineEdit->setValidator(new QIntValidator(ui->PortLineEdit));
    ui->PortLineEdit->setMaxLength(5);

    ui->SndcheckBox->setChecked(true);
    ui->RcvCheckBox->setChecked(true);
}

void TcpServerForm::MapClear()
{
    for(auto it : m_SessionMap) {
        SessionItem* info = it.second;
        if(info) delete info;
    }
    m_SessionMap.clear();
}

void TcpServerForm::on_ListenButton_clicked()
{
    if(bIsListen) {
        if(m_Server->IsStart()) {
            m_Server->Stop();
            ui->ListenButton->setText(QStringLiteral("开始监听"));
            bIsListen = false;

            MapClear();
            ui->TcpListConn->clear();
            ui->TcpLabelConn->setText(QString("0 connected"));

            QString log = QString("%1").arg(u8"服务器停止监听");
            WriteLog(log);
        }
    }
    else {
        m_Port = ui->PortLineEdit->text().toInt();
        if(m_Port >= 1024 && m_Server->Start(m_Port)) {
            ui->ListenButton->setText(QStringLiteral("停止监听"));
            bIsListen = true;

            QString log = QString("%1%2").arg(u8"服务器开始监听,端口:").arg(m_Port);
            WriteLog(log);
        }
        else {
            QMessageBox::information(this, "Waring", u8"监听失败(端口号必须>=1024)");
        }
    }
}

void TcpServerForm::SndToAllSession(QByteArray& data)
{
    std::unordered_map<std::string, SessionItem*>::iterator it;
    for(it = m_SessionMap.begin(); it != m_SessionMap.end(); ++it) {
        SessionItem* session = it->second;
        session->Write(data);
    }
}

void TcpServerForm::SndToSession(QString& session, QByteArray& data)
{
    std::unordered_map<std::string, SessionItem*>::iterator it;
    it = m_SessionMap.find(session.toStdString());
    if(it != m_SessionMap.end()) {
        //std::shared_ptr<SessionItem> session = it->second;
        SessionItem* session = it->second;
        session->Write(data);
    }
}

void TcpServerForm::on_TcpSndButton_clicked()
{
    QString data = ui->TcpSndLineEdit->text();
    QByteArray sdata;

    if(ui->SndcheckBox->isChecked()) {
        sdata = hexStrToByteArray(data);
    }
    else {
        sdata = QByteArray(data.toStdString().c_str());
    }

    if(ui->TcpSndAllCheckBox->isChecked()) {
        SndToAllSession(sdata);
    }
    else {
        int row = ui->TcpListConn->currentRow();
        if(row >= 0) {
            QString item = ui->TcpListConn->item(row)->text();
            SndToSession(item, sdata);
        }
    }
}

SessionItem* TcpServerForm::NewSessionItem(std::shared_ptr<TcpSession> &session)
{
    std::lock_guard<std::mutex> lock(m_Lock);
    SessionItem* item = new SessionItem(session);
    //std::shared_ptr<SessionItem> item = std::make_shared<SessionItem>(session);

    m_SessionMap[item->GetSessionStr().toStdString()] = item;

    return item;
}

void TcpServerForm::AcceptSession(std::shared_ptr<TcpSession> &session)
{
    SessionItem* item = NewSessionItem(session);
    connect(item, &SessionItem::SignalRead, this, &TcpServerForm::SlotRead);
    connect(item, &SessionItem::SignalDisconnected, this, &TcpServerForm::SlotDisconnect);
    //qDebug() << "AcceptSession(TcpServerForm)" << session.use_count();

    ui->TcpListConn->addItem(item->GetSessionStr());
    ui->TcpLabelConn->setText(QString("%1 connected").arg(ui->TcpListConn->count()));

    QString lss = QString("(%1):%2").arg(item->GetSessionStr()).arg("accepted");
    WriteLog(lss);
}

void TcpServerForm::SlotDisconnect(const QString& s)
{
    QString msg = QString("(%1):%2").arg(s).arg("disconnected");
    WriteLog(msg);

    for (int i = 0; i < ui->TcpListConn->count(); i++) {
        if (ui->TcpListConn->item(i)->text() == s) {
            ui->TcpListConn->takeItem(i);
            break;
        }
    }

    ui->TcpLabelConn->setText(QString("%1 connected").arg(ui->TcpListConn->count()));

    DisconnectClear(s);
}

void TcpServerForm::DisconnectClear(const QString& s)
{
    std::lock_guard<std::mutex> lock(m_Lock);
    auto it = m_SessionMap.find(s.toStdString());
    if(it != m_SessionMap.end()) {
        SessionItem* session = it->second;
        //session->disconnect();
        qDebug() << "TcpServerForm::SlotDisconnect";
        disconnect(session, &SessionItem::SignalRead, this, &TcpServerForm::SlotRead);
        disconnect(session, &SessionItem::SignalDisconnected, this, &TcpServerForm::SlotDisconnect);

        delete session;
        session = nullptr;
        it->second = nullptr;
        m_SessionMap.erase(it);
    }
}

void TcpServerForm::SlotRead(const QString& s, const QByteArray& data)
{
    WriteLogNoTime(">>------------------------------------------------------");
    QString msg = QString("%1(%2)%3%4%5:").arg(u8"从") \
            .arg(s).arg(u8"收到数据").arg(data.length()).arg(u8"个");
    WriteLog(msg);

    if(ui->RcvCheckBox->isChecked())
        msg = QString("%1").arg(byteArrayToHexStr(data));
    else
        //msg += QString("%1").arg(byteArrayToAsciiStr(data));
        msg = QString("%1").arg(data.toStdString().c_str());
    WriteLogNoTime(msg);

    if(ui->RcvCheckBox->isChecked()) ParseDeviceFrame(data);
}

const char* TcpServerForm::GetSensorStatusStr(quint8 state)
{
    //0:u"求救恢复", 1:u"求救", 3:u"故障", 10:u"恢复正常", 14:u"浓度报警", 15:u"异常报警"
    const char*buf = u8"未知状态";

    switch(state)
    {
    case 0:  buf = u8"求救恢复"; break;
    case 1:  buf = u8"求救事件"; break;
    case 3:  buf = u8"故障事件"; break;
    case 10: buf = u8"恢复正常"; break;
    case 14: buf = u8"浓度报警"; break;
    case 15: buf = u8"异常报警"; break;
    }

    return buf;
}

void TcpServerForm::ParseDeviceFrame(const QByteArray &data)
{
    #define STRALAIN 4
    #define MINFRAMELEN (24)

    m_FrameBuff.append(data);

    qint32 blen = m_FrameBuff.length();
    QString log = QString("%1:%2").arg(u8"帧缓存长度").arg(blen);
    WriteLogNoTime(log);

    while(blen >= MINFRAMELEN) {
        qint32 i = 0;

        log = QString("%1").arg(u8"数据解包:");
        WriteLogNoTime(log);

        char* buf = m_FrameBuff.data();
        for(i = 0; i < blen - 1; i++) {
            if(static_cast<unsigned char>(buf[i]) == 0xa5
                    && static_cast<unsigned char>(buf[i+1]) == 0x03)
                break;
        }

        if(i > 0) {
            m_FrameBuff.remove(0, i);
            blen -= i;
            log = QString("%1").arg(u8"移除帧头前多余字符");
            WriteLogNoTime(log);

            buf = m_FrameBuff.data();
        }
        if(blen < MINFRAMELEN) {
            log = QString("%1").arg(u8"帧长不够");
            WriteLogNoTime(log);
            break;
        }

        log = QString("%1:%2").arg(u8"Head", STRALAIN).arg("A5 03");
        WriteLogNoTime(log);
        QByteArray tmp = QByteArray(buf+2, 8);
        log = QString("%1:%2").arg(u8"CCID", STRALAIN).arg(byteArrayToHexStr(tmp));
        WriteLogNoTime(log);
        tmp = QByteArray(buf+10, 1);
        log = QString("%1:%2").arg(u8"Cmd", STRALAIN).arg(byteArrayToHexStr(tmp));
        WriteLogNoTime(log);
        tmp = QByteArray(buf+11, 1);
        log = QString("%1:%2").arg(u8"Len", STRALAIN).arg(QByteArray(buf+11, 1).toHex().toInt(nullptr, 16));
        WriteLogNoTime(log);
        log = QString("%1:%2mV").arg(u8"Volt", STRALAIN).arg(QByteArray(buf+12, 2).toHex().toInt(nullptr, 16) * 2);
        WriteLogNoTime(log);

        int data_len = static_cast<unsigned char> (buf[11]);
        if(blen < data_len + 12) {
            data_len = blen - 12;
            log = QString("%1").arg(u8"数据帧丢包", STRALAIN);
            WriteLogNoTime(log);
        }
        //remove head frame
        m_FrameBuff.remove(0, 14);
        buf = m_FrameBuff.data();

        int flen = (data_len - 2) / 10;
        for(int j = 0; j < flen; j++) {
            log = QString("%1:%2 %3:%4 %5:%6 %7:%8-%9-%10 %11:%12:%13]") \
                    .arg(u8"[地址") \
                    .arg(static_cast<unsigned char>(buf[j*10])) \
                    .arg(u8"事件") \
                    .arg(GetSensorStatusStr(static_cast<unsigned char>(buf[j*10+1]))) \
                    .arg(u8"浓度") \
                    .arg(QByteArray(buf+j*10+2, 2).toHex().toShort(nullptr, 16), 5) \
                    .arg(u8"时间") \
                    .arg(static_cast<unsigned int>(buf[j*10+4]), 2, 10, QChar('0')) \
                    .arg(static_cast<unsigned char>(buf[j*10+5]), 2, 10, QChar('0')) \
                    .arg(static_cast<unsigned char>(buf[j*10+6]), 2, 10, QChar('0')) \
                    .arg(static_cast<unsigned char>(buf[j*10+7]), 2, 10, QChar('0')) \
                    .arg(static_cast<unsigned char>(buf[j*10+8]), 2, 10, QChar('0')) \
                    .arg(static_cast<unsigned char>(buf[j*10+9]), 2, 10, QChar('0'));
            WriteLogNoTime(log);
        }
        //remove already parsed frame
        m_FrameBuff.remove(0, data_len - 2);
        blen -= (data_len + 12);
    }
}

void TcpServerForm::WriteLog(QString &str)
{
    QString msg = QString("[%1]%2").arg(TIMEMS).arg(str);
    ui->TcpRcvTxtEdit->appendPlainText(msg);
}

void TcpServerForm::WriteLog(const char* str)
{
    QString msg = QString("[%1]%2").arg(TIMEMS).arg(str);
    ui->TcpRcvTxtEdit->appendPlainText(msg);
}

void TcpServerForm::WriteLogNoTime(QString& str)
{
    QString msg = QString("%1").arg(str);
    ui->TcpRcvTxtEdit->appendPlainText(msg);
}

void TcpServerForm::WriteLogNoTime(const char* str)
{
    QString msg = QString("%1").arg(str);
    ui->TcpRcvTxtEdit->appendPlainText(msg);
}

QString TcpServerForm::byteArrayToAsciiStr(const QByteArray &data)
{
    QString temp;
    int len = data.size();

    for (int i = 0; i < len; i++) {
        //0x20为空格,空格以下都是不可见字符
        char b = data.at(i);

        if (0x00 == b) {
            temp += QString("\\NUL");
        } else if (0x01 == b) {
            temp += QString("\\SOH");
        } else if (0x02 == b) {
            temp += QString("\\STX");
        } else if (0x03 == b) {
            temp += QString("\\ETX");
        } else if (0x04 == b) {
            temp += QString("\\EOT");
        } else if (0x05 == b) {
            temp += QString("\\ENQ");
        } else if (0x06 == b) {
            temp += QString("\\ACK");
        } else if (0x07 == b) {
            temp += QString("\\BEL");
        } else if (0x08 == b) {
            temp += QString("\\BS");
        } else if (0x09 == b) {
            temp += QString("\\HT");
        } else if (0x0A == b) {
            temp += QString("\\LF");
        } else if (0x0B == b) {
            temp += QString("\\VT");
        } else if (0x0C == b) {
            temp += QString("\\FF");
        } else if (0x0D == b) {
            temp += QString("\\CR");
        } else if (0x0E == b) {
            temp += QString("\\SO");
        } else if (0x0F == b) {
            temp += QString("\\SI");
        } else if (0x10 == b) {
            temp += QString("\\DLE");
        } else if (0x11 == b) {
            temp += QString("\\DC1");
        } else if (0x12 == b) {
            temp += QString("\\DC2");
        } else if (0x13 == b) {
            temp += QString("\\DC3");
        } else if (0x14 == b) {
            temp += QString("\\DC4");
        } else if (0x15 == b) {
            temp += QString("\\NAK");
        } else if (0x16 == b) {
            temp += QString("\\SYN");
        } else if (0x17 == b) {
            temp += QString("\\ETB");
        } else if (0x18 == b) {
            temp += QString("\\CAN");
        } else if (0x19 == b) {
            temp += QString("\\EM");
        } else if (0x1A == b) {
            temp += QString("\\SUB");
        } else if (0x1B == b) {
            temp += QString("\\ESC");
        } else if (0x1C == b) {
            temp += QString("\\FS");
        } else if (0x1D == b) {
            temp += QString("\\GS");
        } else if (0x1E == b) {
            temp += QString("\\RS");
        } else if (0x1F == b) {
            temp += QString("\\US");
        } else if (0x7F == b) {
            temp += QString("\\x7F");
        } else if (0x5C == b) {
            temp += QString("\\x5C");
        } else if (0x20 >= b) {
            temp += QString("\\x%1").arg(decimalToStrHex((quint8)b));
        } else {
            temp += QString("%1").arg(b);
        }
    }

    return temp.trimmed();
}

QString TcpServerForm::decimalToStrHex(int decimal)
{
    QString temp = QString::number(decimal, 16);
    if (temp.length() == 1) {
        temp = "0" + temp;
    }

    return temp;
}

QString TcpServerForm::byteArrayToHexStr(const QByteArray &data)
{
    QString temp = "";
    QString hex = data.toHex();

    for (int i = 0; i < hex.length(); i = i + 2) {
        temp += hex.mid(i, 2) + " ";
    }

    return temp.trimmed().toUpper();
}

int TcpServerForm::hexStrToChar(char ch)
{
    if(ch >= '0' && ch <= '9') {
        return ch - '0';
    }

    if(ch >= 'a' && ch <= 'f') {
        return ch - 'a' + 10;
    }

    if(ch >= 'A' && ch <= 'F') {
        return ch - 'A' + 10;
    }

    return 0xff;
}

QByteArray TcpServerForm::hexStrToByteArray(const QString& data)
{
    QByteArray tmp;

    int len = data.length(), i, j;
    tmp.resize(len/2);

    int hhex, lhex;
    for(i = 0, j = 0; i < len;) {
        char hch = data.at(i).toLatin1();
        if(hch == ' ' ) {
            i++;
            continue;
        }
        i++;
        if(i >= len) break;
        hhex = hexStrToChar(hch);

        char lch = data.at(i).toLatin1();
        lhex = hexStrToChar(lch);

        i++;

        //左字符出错 || 右字符出错且不是空格跳过
        if(hhex == 0xff) continue;
        if(lhex == 0xff && lch != ' ') continue;
        //右字符是空格，则左字符按单字符补0处理
        if(lhex != 0xff) {
            hhex = hhex * 16 + lhex;
        }
        tmp[j] = static_cast<char>(hhex);
        j++;
    }

    if(j < len/2) {
        tmp.remove(j, len/2 - j);
    }

    return tmp;
}

void TcpServerForm::on_ClearRecordBtn_clicked()
{
    ui->TcpRcvTxtEdit->clear();
}

void TcpServerForm::on_SaveRecordBtn_clicked()
{
    QString filename = QString("%1/%2.txt").arg(qApp->applicationDirPath()).arg(STRDATETIME);
    QString data = ui->TcpRcvTxtEdit->toPlainText();

    QFile f(filename);
    if(f.open(QFile::WriteOnly | QFile::Text)) {
        f.write(data.toUtf8());
        f.close();
    }

    ui->TcpRcvTxtEdit->clear();
}
